﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using Grpc.Core;

namespace Frontend.Balancer;

public class SubchannelReporter : IObservable<SubchannelReporterResult>
{
    private readonly ObservableCollection<IObserver<SubchannelReporterResult>> _observers;
    private SubchannelReporterResult _lastResult = new SubchannelReporterResult(ConnectivityState.Idle, new List<ReportedSubchannelState>());

    public SubchannelReporter()
    {
        _observers = new ObservableCollection<IObserver<SubchannelReporterResult>>();
        _observers.CollectionChanged += OnObserversChanged;
    }

    private void OnObserversChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        NotifySubscriptersCore();
    }

    public void NotifySubscribers(SubchannelReporterResult result)
    {
        _lastResult = result;

        NotifySubscriptersCore();
    }

    private void NotifySubscriptersCore()
    {
        foreach (var observer in _observers)
        {
            NotifySubscriber(observer, _lastResult);
        }
    }

    private void NotifySubscriber(IObserver<SubchannelReporterResult> observer, SubchannelReporterResult result)
    {
        observer.OnNext(result);
    }

    public IDisposable Subscribe(IObserver<SubchannelReporterResult> observer)
    {
        _observers.Add(observer);
        return new Subscription(this, observer);
    }

    private void Unsubscribe(IObserver<SubchannelReporterResult> observer)
    {
        _observers.Remove(observer);
    }

    private class Subscription : IDisposable
    {
        private readonly SubchannelReporter _reporter;
        private readonly IObserver<SubchannelReporterResult> _observer;

        public Subscription(SubchannelReporter reporter, IObserver<SubchannelReporterResult> observer)
        {
            _reporter = reporter;
            _observer = observer;
        }

        public void Dispose()
        {
            _reporter.Unsubscribe(_observer);
        }
    }
}

public record SubchannelReporterResult(ConnectivityState State, List<ReportedSubchannelState> Subchannels);
